package com.pickgliss.loader
{
	import com.pickgliss.ui.ComponentSetting;
	import com.pickgliss.utils.StringUtils;
	
	import flash.events.EventDispatcher;
	import flash.external.ExternalInterface;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;

	public class LoaderManager extends EventDispatcher
	{
		/**
		 *允许同时加载的loader数量 
		 */		
		public static var ALLOW_MUTI_LOAD_COUNT:int = 3;
		/**
		 *从缓存加载 
		 */		
		public static const LOAD_FROM_LOCAL:int = 2;
		/**
		 *从网页加载 
		 */		
		public static const LOAD_FROM_WEB:int = 1;
		public static const LOAD_NOT_SET:int = 0;
		private static var _instance:LoaderManager;

		public static function get Instance():LoaderManager
		{
			if(_instance == null)
			{
				_instance = new LoaderManager();
			}
			return _instance;
		}

		public function LoaderManager()
		{
			loadedResource	= new Dictionary();
			_loaderSaveByID = new Dictionary();
			_loaderSaveByPath = new Dictionary();
			_loadingLoaderList = new Vector.<BaseLoader>();
			_waitingLoaderList = new Vector.<BaseLoader>();
			_canClearList      = new Vector.<BaseLoader>();
			initLoadMode();
		}

		/**
		 *模块资源是否已加载的Boolean值列表，key-uiModuleType，value-boolean 
		 */		
		public var loadedResource:Dictionary;
		private var _loadMode:int = LOAD_NOT_SET;
		private var _loaderIdCounter:int = 0;
		private var _loaderSaveByID:Dictionary;  //以id为key保存的loader列表
		private var _loaderSaveByPath:Dictionary;  ////以url为key保存的loader列表
		private var _loadingLoaderList:Vector.<BaseLoader>;  //正在加载的loader列表
		private var _waitingLoaderList:Vector.<BaseLoader>;  //等待加载的loader列表
		private var _canClearList : Vector.<BaseLoader>;  //可以清除的loader列表
		
		/**
		 *new Loader  
		 * @param filePath  加载url
		 * @param type  loader类型
		 * @param args  附带参数
		 * @param requestMethod  请求方式
		 */		
		public function creatLoaderByType(filePath:String,type:int,args:URLVariables,requestMethod:String):BaseLoader
		{
			var loader:BaseLoader;
			switch(type)
			{
				case BaseLoader.BITMAP_LOADER:
					loader = new BitmapLoader(getNextLoaderID(),filePath);
					break;
				case BaseLoader.TEXT_LOADER:
					loader = new TextLoader(getNextLoaderID(),filePath,args);
					break;
				case BaseLoader.DISPLAY_LOADER:
					loader = new DisplayLoader(getNextLoaderID(),filePath);
					break;
				case BaseLoader.MOVIE_LOADER:
					loader = new MovieLoader(getNextLoaderID(),filePath);
					break;
				case BaseLoader.BYTE_LOADER:
					loader = new BaseLoader(getNextLoaderID(),filePath);
					break;
				case BaseLoader.COMPRESS_TEXT_LOADER:
					loader = new CompressTextLoader(getNextLoaderID(),filePath,args);
					break;
				case BaseLoader.UN_COMPRESS_TEXT_LOADER:
					loader = new UnCompressTextLoader(getNextLoaderID(),filePath,args);
					break;
				case BaseLoader.MODULE_LOADER:
					loader = new ModuleLoader(getNextLoaderID(),filePath);
					break;
				case BaseLoader.REQUEST_LOADER:
					loader = new RequestLoader(getNextLoaderID(),filePath,args,requestMethod);
					break;
				case BaseLoader.COMPRESS_REQUEST_LOADER:
					loader = new CompressRequestLoader(getNextLoaderID(),filePath,args,requestMethod);
					break;
				case BaseLoader.BYTE_AIM_LOADER:
					loader = new ByteAimLoader(getNextLoaderID(),filePath);
					break;
				case BaseLoader.DOMAIN_RESOURCE_LOADER:
					loader = new DomainResourceLoad(getNextLoaderID(),filePath);
					break;
			}
			return loader;
		}
		
		
		public function clearBswfLoader() : void
		{
			for each(var load : BaseLoader in _loaderSaveByID)
			{
				if(load is DomainResourceLoad)
				{
					_loadingLoaderList.splice(_loadingLoaderList.indexOf(load),1);
					_waitingLoaderList.splice(_waitingLoaderList.indexOf(load),1);
					load.removeEventListener(LoaderEvent.COMPLETE,__onLoadFinish);
					deleteSavedLoader(load);
					load.dispose();
					load = null;
				}
			}
		}
		
		public function getLoadMode():int
		{
			return _loadMode;
		}
		
		/**
		 *创建指定类型loader  
		 * @param filePath  加载url
		 * @param type  loader类型
		 * @param args  附带参数
		 * @param requestMethod  请求方式
		 * @param createNew  是否直接new
		 * @param rnd  随机数
		 */		
		public function creatLoader(filePath:String,type:int,args:URLVariables = null,requestMethod:String = URLRequestMethod.GET, createNew : Boolean = false,rnd:String = null):*
		{
			if(type == BaseLoader.DOMAIN_RESOURCE_LOADER)
			{
				filePath = filePath.replace(".swf",DomainResourceLoad.B_SWF);
			}
			var loader:BaseLoader;
			var fixedVariables:String = fixedVariablesURL(filePath,type,args,rnd);
			loader = getLoaderByURL(fixedVariables,args);
			if(loader == null || createNew)loader = creatLoaderByType(fixedVariables,type,args,requestMethod);
			if(type !=BaseLoader.REQUEST_LOADER && type !=BaseLoader.COMPRESS_REQUEST_LOADER && type != BaseLoader.BITMAP_LOADER && type != BaseLoader.MOVIE_LOADER)
			{
				_loaderSaveByID[loader.id] = loader;
				_loaderSaveByPath[slicePath(loader.url)] = loader;
			}
			return loader;
		}

		/**
		 * 取url问号前面的部分
		 */		
		private function slicePath(path:String):String
		{
			var index:int = path.indexOf("?");
			return index > -1 ? path.slice(0, index) : path;
		}
		
		public function removeCanClearList() : void
		{
			for each(var load : BaseLoader in _canClearList)
			{
				deleteSavedLoader(load);
				load.dispose();
				load = null;
			}
		}
		/**
		 * 删除没有完成加载时就需要删除的loader时调用该方法，如果loader会加载完成，则不能调用
		 * @param loader
		 * 
		 */		
		public function deleteSavedLoader(loader:BaseLoader):void
		{
			delete _loaderSaveByID[loader.id];
			delete _loaderSaveByPath[slicePath(loader.url)];
			if(_canClearList)
			{
				var count : int = _canClearList.length;
				for(var i:int=0;i<count;i++)
				{
					var load : BaseLoader = _canClearList[i] as BaseLoader;
					if(load == loader)
					{
						if(_loadingLoaderList.indexOf(loader) >=0 )
						{
							_loadingLoaderList.splice(_loadingLoaderList.indexOf(load),1);
						}
						load.removeEventListener(LoaderEvent.COMPLETE,__onLoadFinish);
						if(_waitingLoaderList.indexOf(load) >= 0)
							_waitingLoaderList.splice(_waitingLoaderList.indexOf(load),1);
						if(_loadingLoaderList.indexOf(load) >= 0)
							_loadingLoaderList.splice(_loadingLoaderList.indexOf(load),1);
						_canClearList.splice(i,1);
						i--;
						count --;
					}
				}
			}
			
		}
		
		/**
		 * 创建loader并开始加载
		 */		
		public function creatAndStartLoad(filePath:String,type:int,args:URLVariables = null):BaseLoader
		{
			var loader:BaseLoader = creatLoader(filePath,type,args);
			startLoad(loader);
			return loader;
		}
		
		/**
		 * 通过id取得loader
		 */		
		public function getLoaderByID(id:int):BaseLoader
		{
			return _loaderSaveByID[id];
		}
		
		/**
		 * 通过url取得loader
		 */		
		public function getLoaderByURL(url:String,args:URLVariables):BaseLoader
		{
			var loader:BaseLoader = _loaderSaveByPath[slicePath(url)];
			return loader
		}
		
		/**
		 * 生成下一个loader的id
		 */		
		public function getNextLoaderID():int
		{
			return _loaderIdCounter++;
		}
		
		public function saveFileToLocal(loader:BaseLoader):void
		{
			
		}
		
		/**
		 * 开始加载
		 * @param loader  需要执行加载的loader
		 * @param loadImp  是否立即执行加载
		 * @param canClear  能不能清除,针对部分资源，可回收loader
		 */		
		public function startLoad(loader:BaseLoader,loadImp:Boolean = false,canClear:Boolean=false):void
		{
			if(loader)loader.addEventListener(LoaderEvent.COMPLETE,__onLoadFinish);
			if(loader.isComplete)
			{
				loader.dispatchEvent(new LoaderEvent(LoaderEvent.COMPLETE,loader));
				return;
			}
			var md5:String = ComponentSetting.getUrlMd5(loader.url);
			var ba:ByteArray = LoaderSavingManager.loadCachedFile(loader.url,false,md5);
			if(ba && ba.length > 0)
			{
				var key:String = StringUtils.substrSwfName(loader.url);
				if(loader is ModuleLoader)
				{
					if(ComponentSetting.checkNeedAnalyzeMd5(md5))
					{//需要验证的文件
						if(ComponentSetting.compareMD5(ba,md5))
						{
							if(UiConfigAnalyzer.data)
								loader.bytesLoaded = int(UiConfigAnalyzer.data[key + ".swf"]);
						}
						else
						{
							LoaderSavingManager.clearFiles(loader.url);
						}
					}
					else
					{
						if(UiConfigAnalyzer.data)
							loader.bytesLoaded = int(UiConfigAnalyzer.data[key + ".swf"]);
					}
				}
				if(loader.bytesLoaded != 0 && Math.abs(loader.bytesLoaded - ba.length) >= 3000)
				{//如果混村中的文件存在，并且缓存中文件大小和配置的文件大小相差3000字节，则认为缓存中文件错误
					LoaderSavingManager.clearFiles(loader.url);
				}
				else
				{
					loader.loadFromBytes(ba);
					return;
				}
			}
			if(canClear)
			{
				if(_canClearList.indexOf(loader) == -1)_canClearList.push(loader);
			}
			if((_loadingLoaderList.length >= ALLOW_MUTI_LOAD_COUNT && !loadImp )|| getLoadMode() == LOAD_NOT_SET )
			{
				if(_waitingLoaderList.indexOf(loader) == -1)_waitingLoaderList.push(loader);
			}else
			{
				if(_loadingLoaderList.indexOf(loader) == -1)_loadingLoaderList.push(loader);
				if(getLoadMode() == LOAD_FROM_WEB || loader.type == BaseLoader.TEXT_LOADER)
				{
					loader.loadFromWeb();
				}else if(getLoadMode() == LOAD_FROM_LOCAL)
				{
					loader.getFilePathFromExternal();
				}
			}
		}
		
		private function __onLoadFinish(event:LoaderEvent):void
		{
			if(event.loader)
			{
				event.loader.removeEventListener(LoaderEvent.COMPLETE,__onLoadFinish);
				deleteSavedLoader(event.loader);
				_loadingLoaderList.splice(_loadingLoaderList.indexOf(event.loader),1);
			}
			tryLoadWaiting();
		}
		
		private function initLoadMode():void
		{
			if(!ExternalInterface.available)
			{
				setFlashLoadWeb();
				return;
			}
			ExternalInterface.addCallback("SetFlashLoadExternal",setFlashLoadExternal);
			setTimeout(setFlashLoadWeb,200);
		}
		
		private function onExternalLoadStop(id:int,path:String):void
		{
			var loader:BaseLoader = getLoaderByID(id);
			loader.loadFromExternal(path);
		}
		
		private function setFlashLoadExternal():void
		{
			_loadMode = LOAD_FROM_LOCAL;
			ExternalInterface.addCallback("ExternalLoadStop",onExternalLoadStop);
			tryLoadWaiting()
		}
		
		private function setFlashLoadWeb():void
		{
			_loadMode = LOAD_FROM_WEB;
			tryLoadWaiting();
		}
		
		private function tryLoadWaiting():void
		{
			for(var i:int = 0;i<_waitingLoaderList.length;i++)
			{
				if(_loadingLoaderList.length < ALLOW_MUTI_LOAD_COUNT)
				{
					var loader:BaseLoader = _waitingLoaderList.shift();
					startLoad(loader);
				}
			}
		}
		
		public function setup(context:LoaderContext,textLoaderKey:String):void
		{
			DisplayLoader.Context = context;
			TextLoader.TextLoaderKey = textLoaderKey;
			LoaderSavingManager.setup();
		}
		
		/**
		 * 后台加载取得带版本号的地址，防止从浏览器缓存中拿到旧资源 
		 */		
		public function getBackResourceLoadPath(value:String,type:int,variables:URLVariables=null,rnd:String=null):String
		{
			return fixedVariablesURL(value,type,variables,rnd);
		}
		
		/**
		 * 对url进行版本号、随机数处理
		 */		
		private function fixedVariablesURL(path:String,type:int,variables:URLVariables,rnd:String = null):String
		{
			if(type != BaseLoader.REQUEST_LOADER && type != BaseLoader.COMPRESS_REQUEST_LOADER) 
			{
				var variableString:String = "";
				variables = new URLVariables();
				if(type == BaseLoader.BYTE_LOADER || type == BaseLoader.DISPLAY_LOADER
					|| type == BaseLoader.BITMAP_LOADER || type == BaseLoader.MODULE_LOADER
					||type == BaseLoader.MOVIE_LOADER ||type == BaseLoader.DOMAIN_RESOURCE_LOADER)
				{
					var md5:String = ComponentSetting.getUrlMd5(path);
					variables["lv"] = md5;
				}
				else if(type == BaseLoader.COMPRESS_TEXT_LOADER || type == BaseLoader.TEXT_LOADER)
				{
					variables["rnd"] = (rnd == null) ? TextLoader.TextLoaderKey : rnd;
				}
				
				for(var p:String in variables)
				{
					variableString+=(p+"="+variables[p]+"&");
				}
				if (path.indexOf("?") == -1 && variableString != "")
					return path+"?"+variableString;
				else 
					return path + variableString;
			}else
			{
				return path;
			}
		}
	}
}